index.js ➔ resample   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 45
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 34
dl 0
loc 45
rs 9.064
c 0
b 0
f 0
cc 4
1
/*
2
 * Copyright (c) 2019 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview The resample function.
27
 * @see https://github.com/rochars/wave-resampler
28
 */
29
30
import { Interpolator } from './lib/interpolator';
31
import { FIRLPF } from './lib/fir-lpf';
32
import { ButterworthLPF } from './lib/butterworth-lpf';
33
34
/**
35
 * Configures wich resampling method uses LPF by default.
36
 * @private
37
 */
38
const DEFAULT_LPF_USE = {
39
  'point': false,
40
  'linear': false,
41
  'cubic': true,
42
  'sinc': true
43
};
44
45
/**
46
 * The default orders for the LPF types.
47
 * @private
48
 */
49
const DEFAULT_LPF_ORDER = {
50
  'IIR': 16,
51
  'FIR': 71
52
};
53
54
/**
55
 * The classes to use with each LPF type.
56
 * @private
57
 */
58
const DEFAULT_LPF = {
59
  'IIR': ButterworthLPF,
60
  'FIR': FIRLPF
61
};
62
63
/**
64
 * Change the sample rate of the samples to a new sample rate.
65
 * @param {!Array|!TypedArray} samples The original samples.
66
 * @param {number} oldSampleRate The original sample rate.
67
 * @param {number} sampleRate The target sample rate.
68
 * @param {?Object} details The extra configuration, if needed.
69
 * @return {!Float64Array} the new samples.
70
 */
71
export function resample(samples, oldSampleRate, sampleRate, details={}) {  
72
  // Make the new sample container
73
  let rate = ((sampleRate - oldSampleRate) / oldSampleRate) + 1;
74
  let newSamples = new Float64Array(samples.length * (rate));
75
  // Create the interpolator
76
  details.method = details.method || 'cubic';
77
  let interpolator = new Interpolator(
78
    samples.length,
79
    newSamples.length,
80
    {
81
      method: details.method,
82
      tension: details.tension || 0,
83
      sincFilterSize: details.sincFilterSize || 6,
84
      sincWindow: details.sincWindow || undefined
85
    });
86
  // Resample + LPF
87
  if (details.LPF === undefined) {
88
    details.LPF = DEFAULT_LPF_USE[details.method];
89
  } 
90
  if (details.LPF) {
91
    details.LPFType = details.LPFType || 'IIR';
92
    const LPF = DEFAULT_LPF[details.LPFType];
93
    // Upsampling
94
    if (sampleRate > oldSampleRate) {
95
      let filter = new LPF(
96
        details.LPFOrder || DEFAULT_LPF_ORDER[details.LPFType],
97
        sampleRate,
98
        (oldSampleRate / 2));
99
      upsample_(
100
        samples, newSamples, interpolator, filter);
101
    // Downsampling
102
    } else {
103
      let filter = new LPF(
104
        details.LPFOrder || DEFAULT_LPF_ORDER[details.LPFType],
105
        oldSampleRate,
106
        sampleRate / 2);
107
      downsample_(
108
        samples, newSamples, interpolator, filter);
109
    }
110
  // Resample, no LPF
111
  } else {
112
    resample_(samples, newSamples, interpolator);
113
  }
114
  return newSamples;
115
}
116
117
/**
118
 * Resample.
119
 * @param {!Array|!TypedArray} samples The original samples.
120
 * @param {!Float64Array} newSamples The container for the new samples.
121
 * @param {Object} interpolator The interpolator.
122
 * @private
123
 */
124
function resample_(samples, newSamples, interpolator) {
125
  // Resample
126
  for (let i = 0, len = newSamples.length; i < len; i++) {
127
    newSamples[i] = interpolator.interpolate(i, samples);
128
  }
129
}
130
131
/**
132
 * Upsample with LPF.
133
 * @param {!Array|!TypedArray} samples The original samples.
134
 * @param {!Float64Array} newSamples The container for the new samples.
135
 * @param {Object} interpolator The interpolator.
136
 * @param {Object} filter The LPF object.
137
 * @private
138
 */
139
function upsample_(samples, newSamples, interpolator, filter) {
140
  // Resample and filter
141
  for (let i = 0, len = newSamples.length; i < len; i++) {
142
    newSamples[i] = filter.filter(interpolator.interpolate(i, samples));
143
  }
144
  // Reverse filter
145
  filter.reset();
146
  for (let i = newSamples.length - 1; i >= 0; i--) {
147
    newSamples[i]  = filter.filter(newSamples[i]);
148
  }
149
}
150
151
/**
152
 * Downsample with LPF.
153
 * @param {!Array|!TypedArray} samples The original samples.
154
 * @param {!Float64Array} newSamples The container for the new samples.
155
 * @param {Object} interpolator The interpolator.
156
 * @param {Object} filter The LPF object.
157
 * @private
158
 */
159
function downsample_(samples, newSamples, interpolator, filter) {
160
  // Filter
161
  for (let i = 0, len = samples.length; i < len; i++) {
162
    samples[i]  = filter.filter(samples[i]);
163
  }
164
  // Reverse filter
165
  filter.reset();
166
  for (let i = samples.length - 1; i >= 0; i--) {
167
    samples[i]  = filter.filter(samples[i]);
168
  }
169
  // Resample
170
  resample_(samples, newSamples, interpolator);
171
}
172